package cn.topcodes.tcsf.admin.scheduling;

import cn.topcode.unicorn.utils.SpringContextHolder;
import cn.topcode.unicorn.utils.StringUtil;
import com.google.common.base.Strings;
import org.quartz.*;
import org.quartz.impl.matchers.GroupMatcher;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.scheduling.quartz.SchedulerFactoryBean;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.Assert;
import javax.annotation.PostConstruct;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.List;
import java.util.Set;

/**
 * 任务调度服务
 * @author unicorn
 */
public class ScheduleServiceImpl implements ScheduleService {
	

	public final static Logger logger = LoggerFactory.getLogger(ScheduleServiceImpl.class);
	
	@Autowired
	private SchedulerFactoryBean schedulerFactoryBean;

	/**
	 * 初始化
	 * @throws Exception
	 */
	@PostConstruct
	public void init() throws SchedulerException {
		//	添加任务状态监听器
		Scheduler scheduler = schedulerFactoryBean.getScheduler();
		TaskSchedulerListener schedulerListener = new TaskSchedulerListener();
		scheduler.getListenerManager().addSchedulerListener(schedulerListener);
	}

	/**
	 * 通过反射调用scheduleJob中定义的方法
	 * @param scheduleJob
	 */
	static void invokMethod(ScheduleJob scheduleJob, Object... params) {
		Object object = null;
		Class<?> clazz = null;
		//springId不为空先按springId查找bean
		if (!Strings.isNullOrEmpty(scheduleJob.getBeanId())) {
			object = SpringContextHolder.getBean(scheduleJob.getBeanId());
		} else if (!Strings.isNullOrEmpty(scheduleJob.getBeanClass())) {
			try {
				clazz = Class.forName(scheduleJob.getBeanClass());
				object = clazz.newInstance();
			} catch (Exception e) {
				e.printStackTrace();
			}
		}

		if (object == null) {
			logger.error("任务[" + scheduleJob.getJobName() + "]未启动成功，请检查是否配置正确！！！");
			return;
		}
		clazz = object.getClass();
		Method method = null;
		try {
			Class[] types = scheduleJob.getParamTypes() != null ? scheduleJob.getParamTypes() : new Class[0];
			method = clazz.getDeclaredMethod(scheduleJob.getMethodName(), types);
		} catch (NoSuchMethodException e) {
			logger.error("任务名称[" + scheduleJob.getJobName() + "]未启动成功，方法名设置错误！！！");
		} catch (SecurityException e) {
			e.printStackTrace();
		}
		if (method != null) {
			try {
				method.invoke(object, params);
			} catch (Exception e) {
				throw new IllegalStateException("计划任务执行失败", e);
			}
		}

	}

	/**
	 * 添加任务
	 * @param job
	 * @throws SchedulerException
	 */
	@Override
	@Transactional(readOnly = false, rollbackFor = Exception.class)
	public void addJob(ScheduleJob job) {
		Assert.notNull(job);
		try {
			//	若任务已存在则不添加
			ScheduleJob jobInQuartz = this.findBy(job.getJobGroup(), job.getJobName());
			if (jobInQuartz != null) {
				return;
			}

			Scheduler scheduler = schedulerFactoryBean.getScheduler();

			TriggerKey triggerKey = TriggerKey.triggerKey(job.getJobName(), job.getJobGroup());

			CronTrigger trigger = (CronTrigger) scheduler.getTrigger(triggerKey);

			// 触发器不存在，创建一个
			if (null == trigger) {
				//	判断任务是否具有状态
				Class<? extends Job> clazz = job.getIsConcurrent() ? QuartzJobFactory.class : QuartzJobFactoryDisallowConcurrentExecution.class;

				//	建立任务信息对象
				JobDetail jobDetail = JobBuilder.newJob(clazz).withIdentity(job.getJobName(), job.getJobGroup()).build();
				jobDetail.getJobDataMap().put("scheduleJob", job);

				//	构建任务触发器
				CronScheduleBuilder scheduleBuilder = CronScheduleBuilder.cronSchedule(job.getCronExpression());
				trigger = TriggerBuilder.newTrigger().withIdentity(job.getJobName(), job.getJobGroup()).withSchedule(scheduleBuilder).build();

				//	调度任务
				scheduler.scheduleJob(jobDetail, trigger);
			} else {
				// Trigger已存在，那么更新相应的定时设置
				CronScheduleBuilder scheduleBuilder = CronScheduleBuilder.cronSchedule(job.getCronExpression());

				// 按新的cronExpression表达式重新构建trigger
				trigger = trigger.getTriggerBuilder().withIdentity(triggerKey).withSchedule(scheduleBuilder).build();

				// 按新的trigger重新设置job执行
				scheduler.rescheduleJob(triggerKey, trigger);
			}

			if(ScheduleJob.STATUS_PAUSED.equals(job.getJobStatus())) {
				pauseJob(job.getJobGroup(), job.getJobName());
			}
		}catch(Exception e) {
			if(e.getMessage().contains("never fire")) {
				logger.warn("添加计划任务失败！任务已过期！" + job.getJobGroup() + "-" + job.getJobName());
			}else {
				throw new IllegalStateException("添加计划任务失败", e);
			}
		}
	}


	/**
	 * 获取所有计划中的任务列表
	 * @return
	 * @throws SchedulerException
	 */
	@Override
	public List<ScheduleJob> getAllJob() {
		try {
			Scheduler scheduler = schedulerFactoryBean.getScheduler();
			GroupMatcher<JobKey> matcher = GroupMatcher.anyJobGroup();
			Set<JobKey> jobKeys = scheduler.getJobKeys(matcher);
			List<ScheduleJob> jobList = new ArrayList<ScheduleJob>();
			for (JobKey jobKey : jobKeys) {
				List<? extends Trigger> triggers = scheduler.getTriggersOfJob(jobKey);
				for (Trigger trigger : triggers) {
					ScheduleJob job = new ScheduleJob();
					job.setJobName(jobKey.getName());
					job.setJobGroup(jobKey.getGroup());
					Trigger.TriggerState triggerState = scheduler.getTriggerState(trigger.getKey());
					job.setJobStatus(triggerState.name());
					if (trigger instanceof CronTrigger) {
						CronTrigger cronTrigger = (CronTrigger) trigger;
						String cronExpression = cronTrigger.getCronExpression();
						job.setCronExpression(cronExpression);
					}
					jobList.add(job);
				}
			}
			return jobList;
		}catch(Exception e) {
			throw new IllegalStateException("获取计划任务失败", e);
		}
	}

	/**
	 * 所有正在运行的job
	 * @return
	 * @throws SchedulerException
	 */
	@Override
	public List<ScheduleJob> getRunningJob() {
		try {
			Scheduler scheduler = schedulerFactoryBean.getScheduler();
			List<JobExecutionContext> executingJobs = scheduler.getCurrentlyExecutingJobs();
			List<ScheduleJob> jobList = new ArrayList<ScheduleJob>(executingJobs.size());
			for (JobExecutionContext executingJob : executingJobs) {
				ScheduleJob job = new ScheduleJob();
				JobDetail jobDetail = executingJob.getJobDetail();
				JobKey jobKey = jobDetail.getKey();
				Trigger trigger = executingJob.getTrigger();
				job.setJobName(jobKey.getName());
				job.setJobGroup(jobKey.getGroup());
				Trigger.TriggerState triggerState = scheduler.getTriggerState(trigger.getKey());
				job.setJobStatus(triggerState.name());
				if (trigger instanceof CronTrigger) {
					CronTrigger cronTrigger = (CronTrigger) trigger;
					String cronExpression = cronTrigger.getCronExpression();
					job.setCronExpression(cronExpression);
				}
				jobList.add(job);
			}
			return jobList;
		}catch(Exception e) {
			throw new IllegalStateException("获取运行中计划任务失败", e);
		}
	}

	/**
	 * 暂停一个job
	 *
	 * @param group	任务组
	 * @param name 任务名
	 */
	@Override
	public void pauseJob(String group, String name) {
		try {
			Scheduler scheduler = schedulerFactoryBean.getScheduler();
			JobKey jobKey = JobKey.jobKey(name, group);
			scheduler.pauseJob(jobKey);
		}catch(Exception e) {
			throw new IllegalStateException("暂停计划任务失败", e);
		}
	}

	/**
	 * 恢复一个job
	 *
	 * @param group	任务组
	 * @param name 任务名
	 */
	@Override
	public void resumeJob(String group, String name) {
		try {
			Scheduler scheduler = schedulerFactoryBean.getScheduler();
			JobKey jobKey = JobKey.jobKey(name, group);
			scheduler.resumeJob(jobKey);
		}catch(Exception e) {
			throw new IllegalStateException("恢复计划任务失败", e);
		}
	}

	/**
	 * 删除一个job
	 * 
	 * @param group	任务组
	 * @param name 任务名
	 */
	@Override
	public void deleteJob(String group, String name) {
		Assert.hasText(group, "任务组名称不能为空");

		try {
			Scheduler scheduler = schedulerFactoryBean.getScheduler();
			if(StringUtil.isBlank(name)) {
				for (JobKey jobKey : scheduler.getJobKeys(GroupMatcher.jobGroupEquals(group))) {
					scheduler.deleteJob(jobKey);
				}
			}else {
				ScheduleJob job = this.findBy(group, name);
				if(job != null) {
					JobKey jobKey = JobKey.jobKey(name, group);
					scheduler.deleteJob(jobKey);
				}
			}

		}catch(Exception e) {
			throw new IllegalStateException("删除计划任务失败", e);
		}

	}

	/**
	 * 立即执行job
	 * 
	 * @param group	任务组
	 * @param name 	任务名
	 */
	@Override
	public void runAJobNow(String group, String name) {
		try {
			Scheduler scheduler = schedulerFactoryBean.getScheduler();
			JobKey jobKey = JobKey.jobKey(name, group);
			scheduler.triggerJob(jobKey);
		}catch(Exception e) {
			throw new IllegalStateException("立即执行计划任务失败", e);
		}
	}

	/**
	 * 更新job时间表达式
	 * 
	 * @param group 	任务组
	 * @param name 		任务名
	 * @param cron 		cron表达式
	 */
	@Override
	public void updateJobCron(String group, String name, String cron) {
		try {
			Scheduler scheduler = schedulerFactoryBean.getScheduler();

			TriggerKey triggerKey = TriggerKey.triggerKey(name, group);

			CronTrigger trigger = (CronTrigger) scheduler.getTrigger(triggerKey);

			CronScheduleBuilder scheduleBuilder = CronScheduleBuilder.cronSchedule(cron);

			trigger = trigger.getTriggerBuilder().withIdentity(triggerKey).withSchedule(scheduleBuilder).build();

			scheduler.rescheduleJob(triggerKey, trigger);
		}catch(Exception e) {
			throw new IllegalStateException("更新计划任务时间失败", e);
		}
	}

	/**
	 * 检查cron表达式
	 * @param cron	cron表达式
	 */
	@Override
	public void checkCron(String cron) {
		try {
			CronScheduleBuilder scheduleBuilder = CronScheduleBuilder.cronSchedule(cron);
		}catch (Exception e) {
			throw new IllegalArgumentException("Cron表达式无法解析");
		}
	}

	/**
	 * 获取任务信息
	 * @param group	任务组
	 * @param name	任务名
	 * @return	任务信息
	 */
	@Override
	public ScheduleJob findBy(String group, String name) {
		try {
			JobKey jobKey = new JobKey(name, group);
			Scheduler scheduler = this.schedulerFactoryBean.getScheduler();
			List<? extends Trigger> triggers = scheduler.getTriggersOfJob(jobKey);
			for (Trigger trigger : triggers) {
				ScheduleJob job = new ScheduleJob();
				job.setJobName(jobKey.getName());
				job.setJobGroup(jobKey.getGroup());
				Trigger.TriggerState triggerState = scheduler.getTriggerState(trigger.getKey());
				job.setJobStatus(triggerState.name());
				if (trigger instanceof CronTrigger) {
					CronTrigger cronTrigger = (CronTrigger) trigger;
					String cronExpression = cronTrigger.getCronExpression();
					job.setCronExpression(cronExpression);
				}
				return job;
			}
		}catch (Exception e) {
			e.printStackTrace();
		}
		return null;
	}

    public void start() {
		schedulerFactoryBean.start();
    }
}